`default_nettype none

module toplevel(
		// 10MHz base clock
		input wire clk,

		// frontpanel
		input wire [7:0] hi_in,
		output wire [1:0] hi_out,
		inout wire [15:0] hi_inout,
		output wire   i2c_sda,
		output wire   i2c_scl,
		output wire   hi_muxsel,

		// counter input lines
		input wire trig,
		input wire gate,
		
		// pulse output line
		output reg pulse,

		// debug
		output wire flag,
		output wire flagG,
		output reg write,
		output reg photon
		);

   // for trigger input, we use two flip-flops with their outputs crossed
   // back to their inputs, to form a buffer for the edge, that can be cleared by the
   // main program once the event is registered.
   
   // flip-flops for edge buffer
   reg 			      set;
   reg 			      clr;

   // flag for edge buffer
   //wire 		      flag;
   
   assign flag = set ^ clr;

   // edges on trigger lines set the respective flags
   always @( posedge trig )
     set <= ~clr;

   // reset flag after one clock cycle delay to avoid bistability in crossed flip-flops
   //reg 			      photon;
   always @( posedge clk ) begin
      if ( flag ) begin
	 if ( photon ) begin
	    clr <= set;
	    photon <= 0;
	 end
	 else begin
	    photon <= 1;
	 end
      end
   end // always @ ( posedge clk )



   // flip-flops for edge buffer
   reg 			      setG;
   reg 			      clrG;

   // flag for edge buffer
   //wire 		      flagG;
   
   assign flagG = setG ^ clrG;

   // edges on trigger lines set the respective flags
   always @( posedge gate )
     setG <= ~clrG;
   always @( negedge gate )
     clrG <= setG;


   reg 			      gate_state;
   
   always @( posedge clk )
     if ( flagG )
       gate_state <= 1;
     else
       gate_state <= 0;

   
   // photon counter
   reg [15:0] 		      counter;

   wire 		      pulse_enable;
   wire 		      stream_enable;
   wire [15:0] 		      threshold;
   
   // fifo write line. We will assign write for one cycle of the base clock to write data into the fifo.
   // We will write when gate has been hig hand low during two successive clock cycles.
   //reg 			      write;
   
   wire [15:0] 		      pulse_length;
   reg [15:0] 		      pulse_counter;


   // state machine for processing count / write / pulse task
   reg [2:0] 		      state;
   
   // states
   parameter STATE_IDLE       = 3'd0;
   parameter STATE_COUNT      = 3'd1;
   parameter STATE_WRITE      = 3'd2;
   parameter STATE_PULSE      = 3'd3;
   
   
   always @( posedge clk )
     begin
	case ( state )
	  STATE_IDLE:
	    begin
	       write <= 0;
	       pulse <= 0;
	       counter <= 0;
	       if ( gate_state )
		 state <= STATE_COUNT;
	    end
	  STATE_COUNT:
	    begin
	       if ( photon )
		 counter <= counter + 1;
	       if ( ~gate_state )
		 state <= STATE_WRITE;
	    end
	  STATE_WRITE:
	    begin
	       if ( stream_enable )
		 write <= 1;
	       if ( pulse_enable && counter < threshold )
		 begin
		    pulse <= 1;
		    pulse_counter <= pulse_length;
		    state <= STATE_PULSE;
		 end
	       else
		 state <= STATE_IDLE;
	    end
	  STATE_PULSE:
	    begin
	       write <= 0;
	       if ( pulse_counter == 0 )
		 state <= STATE_IDLE;
	       else
		 pulse_counter <= pulse_counter - 1;
	    end
	endcase // case ( state )
     end
    
   // USB interface
   wire                       ti_clk; // USB interface clock
   wire [30:0] 		      ok1; // data from HostInterface
   wire [16:0] 		      ok2; // data into HostInterface
   wire [17*3-1:0] 	      ok2x; // input muxer

   assign i2c_sda = 1'bz;
   assign i2c_scl = 1'bz;
   assign hi_muxsel = 1'b0;

   okWireOR # (.N(3)) wireOR (ok2, ok2x);
   okHost okHI ( .hi_in( hi_in ),
		 .hi_out( hi_out ),
		 .hi_inout( hi_inout ), // connections for USB controller
		 .ti_clk( ti_clk ),
		 .ok1( ok1 ),
		 .ok2( ok2 ) ); // connections for endpoints

   okWireIn  ep02 ( .ok1( ok1 ),
		    .ep_addr( 8'h02 ),
		    .ep_dataout( threshold ) );

   okWireIn  ep03 ( .ok1( ok1 ),
		    .ep_addr( 8'h03 ),
		    .ep_dataout( { pulse_enable, stream_enable } ) );

   okWireIn  ep04 ( .ok1( ok1 ),
		    .ep_addr( 8'h04 ),
		    .ep_dataout( pulse_length ) );

   okWireOut ep20 ( .ok1( ok1 ),
		    .ok2( ok2x[1*17 +: 17] ),
		    .ep_addr( 8'h20 ),
		    .ep_datain( counter ) );

       
   wire 		      pipe_block;
   wire 		      pipe_read;		

   wire 		      fifo_rst;
   wire [15:0] 		      fifo_dout;
   wire 		      fifo_prog_full;
   wire [9:0] 		      fifo_prog_full_thresh;
   wire 		      fifo_empty;
   wire 		      fifo_full;
   wire 		      fifo_overflow;
   wire 		      fifo_underflow;
   
   // output of fifo flags
   okWireOut ep21 ( .ok1( ok1 ),
		    .ok2( ok2x[2*17 +: 17] ),
		    .ep_addr( 8'h21 ),
		    .ep_datain( {state, fifo_prog_full, fifo_overflow, fifo_underflow, fifo_full, fifo_empty} ) );

   // fifo threshold
   okWireIn  ep00 ( .ok1(ok1),
		    .ep_addr(8'h00),
		    .ep_dataout( fifo_prog_full_thresh )   );

   // fifo threshold
   okTriggerIn ep40 ( .ok1( ok1 ),
		      .ep_clk( clk ),
		      .ep_addr( 8'h40 ),
		      .ep_trigger( fifo_rst )   );

   // pipe
   okBTPipeOut  epA0 ( .ok1(ok1),
		       .ok2(ok2x[ 0*17 +: 17 ]),
		       .ep_addr(8'ha0),
		       .ep_read( pipe_read ),
		       .ep_blockstrobe( pipe_block ),
		       .ep_datain( fifo_dout ),
		       .ep_ready( fifo_prog_full )  );


   // fifo buffer
   fifo Buffer ( .din( counter ),
		 .prog_full_thresh( fifo_prog_full_thresh ),
		 .rd_clk( ti_clk ),
		 .rd_en( pipe_read ),
		 .rst( fifo_rst ),
		 .wr_clk( clk ),
		 .wr_en( write ),
		 .dout( fifo_dout ),
		 .empty( fifo_empty ),
		 .full( fifo_full ),
		 .overflow( fifo_overflow ),
		 .prog_full( fifo_prog_full ),
		 .underflow( fifo_underflow )    );
 
endmodule
